Caffeine cache 实例代码demo

简介

点击查看 https://www.javazhiyin.com/18782.html

填充策略(Population)实例

Cache 手动加载

Cache 测试代码
        Cache<Object, Object> cache = Caffeine.newBuilder()
                //基于时间失效->写入之后开始计时失效
                .expireAfterWrite(2000, TimeUnit.MILLISECONDS)
                .build();

        //返回 key + 当前时间戳作为 value
        Function<Object, Object> getFuc = key -> key + "_" + System.currentTimeMillis();

        String key1 = "key1";

        //获取 key1 对应的值,如果获取不到则执行 getFuc
        Object value = cache.get(key1, getFuc);
        System.out.println(value);

        //让缓存到期
        sleep(2001);

        //获取 key1 对应的值,如果获取不到则返回 null
        value = cache.getIfPresent(key1);
        System.out.println(value);

        //设置 key1 的值
        cache.put(key1, "putValue");
        value = cache.get(key1, getFuc);
        System.out.println(value);

        ConcurrentMap<Object, Object> asMap = cache.asMap();
        System.out.println(asMap);

        //删除 key1
        cache.invalidate(key1);
        asMap = cache.asMap();
        System.out.println(asMap);
Cache 测试结果
key1_1568188595546
null
putValue
{key1=putValue}
{}

Process finished with exit code 0

LoadingCache 同步方法加载

LoadingCache 测试代码
        LoadingCache<Object, Object> cache = Caffeine.newBuilder()
                //基于时间失效->写入之后开始计时失效
                .expireAfterWrite(2000, TimeUnit.MILLISECONDS)
                //同步加载和手动加载的区别就是在构建缓存时提供一个同步的加载方法
                .build(new CacheLoader<Object, Object>() {
                    //单个 key 的值加载
                    @Nullable
                    @Override
                    public Object load(@NonNull Object key) throws Exception {
                        return key + "_" + System.currentTimeMillis();
                    }

                    //如果没有重写 loadAll 方法则默认的 loadAll 回循环调用 load 方法,一般重写优化性能
                    @Override
                    public @NonNull Map<Object, Object> loadAll(@NonNull Iterable<?> keys) throws Exception {
                        Map<Object, Object> data = new HashMap<>();
                        for (Object key : keys) {
                            data.put(key, key + "_all_" + System.currentTimeMillis());
                        }
                        return data;
                    }
                });

        String key1 = "key1";

        //获取 key1 对应的值
        Object value = cache.get(key1);
        System.out.println(value);

        sleep(2001);

        //批量获取
        Map<Object, Object> all = cache.getAll(Arrays.asList("key1", "key2", "key3"));
        System.out.println(all);
LoadingCache 测试结果
---exec load---
key1_1568187805873
---exec loadAll---
{key1=key1_all_1568187807879, key2=key2_all_1568187807879, key3=key3_all_1568187807879}

Process finished with exit code 0

AsyncCache 异步手动加载

AsyncCache 测试代码
        AsyncCache<Object, Object> asyncCache = Caffeine.newBuilder()
                .expireAfterWrite(2000, TimeUnit.MILLISECONDS)
                .buildAsync();

        //返回 key + 当前时间戳作为 value
        Function<Object, Object> getFuc = key -> key + "_" + System.currentTimeMillis();

        String key1 = "key1";

        //异步手动加载返回的不是值,而是 CompletableFuture
        CompletableFuture<Object> future = asyncCache.get(key1, getFuc);
        //异步获取结果,对高性能场景有帮助
        future.thenAccept(new Consumer<Object>() {
            @Override
            public void accept(Object o) {
                //输出结果可以看到打印时间比生成时间晚
                System.out.println(System.currentTimeMillis() + "->" + o);
            }
        });

        sleep(4000);

        //如果 cache 中 key 为空,直接返回 null,不为空则异步取值
        CompletableFuture<Object> ifPresent = asyncCache.getIfPresent(key1);
        if (ifPresent == null) {
            System.out.println("null");
        } else {
            //异步取值
            ifPresent.thenAccept(new Consumer<Object>() {
                @Override
                public void accept(Object o) {
                    System.out.println(o);
                }
            });
        }
        //批量异步取值,取不到则加载值
        CompletableFuture<Map<Object, Object>> all = asyncCache.getAll(Arrays.asList("key1", "key2", "key3"), new Function<Iterable<?>, Map<Object, Object>>() {
            @Override
            public Map<Object, Object> apply(Iterable<?> keys) {
                Map<Object, Object> data = new HashMap<>();
                for (Object key : keys) {
                    data.put(key, key + "_all_" + System.currentTimeMillis());
                }
                return data;
            }
        });
        //批量异步获取
        all.thenAccept(new Consumer<Map<Object, Object>>() {
            @Override
            public void accept(Map<Object, Object> objectObjectMap) {
                System.out.println(objectObjectMap);
            }
        });

        sleep(2001);

        ConcurrentMap<Object, CompletableFuture<Object>> asMap = asyncCache.asMap();
        System.out.println(asMap);
AsyncCache 测试结果
1568191332416->key1_1568191332416
null
{key1=key1_all_1568191336422, key2=key2_all_1568191336422, key3=key3_all_1568191336422}
{}

Process finished with exit code 0

AsyncLoadingCache 异步方法加载

AsyncLoadingCache 测试代码
        AsyncLoadingCache<Object, Object> asyncLoadingCache = Caffeine.newBuilder()
                //基于时间失效->写入之后开始计时失效
                .expireAfterWrite(2000, TimeUnit.MILLISECONDS)
                //同步加载和手动加载的区别就是在构建缓存时提供一个同步的加载方法
                .buildAsync(new CacheLoader<Object, Object>() {
                    //单个 key 的值加载
                    @Nullable
                    @Override
                    public Object load(@NonNull Object key) throws Exception {
                        System.out.println("---exec load---");
                        return key + "_" + System.currentTimeMillis();
                    }

                    //如果没有重写 loadAll 方法则默认的 loadAll 回循环调用 load 方法,一般重写优化性能
                    @Override
                    public @NonNull Map<Object, Object> loadAll(@NonNull Iterable<?> keys) throws Exception {
                        System.out.println("---exec loadAll---");
                        Map<Object, Object> data = new HashMap<>();
                        for (Object key : keys) {
                            data.put(key, key + "_all_" + System.currentTimeMillis());
                        }
                        return data;
                    }
                });

        String key1 = "key1";

        //获取值,为空则执行异步加载方法
        CompletableFuture<Object> future = asyncLoadingCache.get(key1);
        //异步获取值
        future.thenAccept(new Consumer<Object>() {
            @Override
            public void accept(Object o) {
                System.out.println(System.currentTimeMillis() + "->" + o);
            }
        });
        //批量异步获取
        CompletableFuture<Map<Object, Object>> all = asyncLoadingCache.getAll(Arrays.asList("key1", "key2", "key3"));
        all.thenAccept(new Consumer<Map<Object, Object>>() {
            @Override
            public void accept(Map<Object, Object> objectObjectMap) {
                System.out.println(objectObjectMap);
            }
        });
        sleep(4000);
        ConcurrentMap<Object, CompletableFuture<Object>> asMap = asyncLoadingCache.asMap();
        System.out.println(asMap);
AsyncLoadingCache 测试结果
---exec load---
1568191839395->key1_1568191839387
---exec loadAll---
{key1=key1_1568191839387, key2=key2_all_1568191839400, key3=key3_all_1568191839400}
{}

Process finished with exit code 0

驱逐策略(eviction)实例

基于大小(Size-based)

基于条数

当缓存条数超过预设值,发生驱逐

        Cache<Object, Object> cache = Caffeine.newBuilder()
                //缓存最大条数,超过这个条数就是驱逐缓存
                .maximumSize(20)
                .removalListener(new RemovalListener<Object, Object>() {
                    @Override
                    public void onRemoval(@Nullable Object k, @Nullable Object v, @NonNull RemovalCause removalCause) {
                        System.out.println("removed " + k + " cause " + removalCause.toString());
                    }
                })
                .build();

        for (int i = 0; i < 25; i++) {
            cache.put(i, i + "_value");
        }
        cache.cleanUp();

结果,可以看到当缓存条数达到指定值后,如果继续放缓存则开始驱逐

removed 0 cause SIZE
removed 1 cause SIZE
removed 2 cause SIZE
removed 3 cause SIZE
removed 4 cause SIZE

Process finished with exit code 0
基于权重

简单来说就是你可以自己设置一个规则,根据缓存对象计算其不同的权重值,当缓存内所有对象的权重值加起来超过预设值就会发生驱逐。

        Cache<Object, Object> cache = Caffeine.newBuilder()
                //缓存最大权重值
                .maximumWeight(150)
                //自定义计算权重
                .weigher(new Weigher<Object, Object>() {
                    @Override
                    public @NonNegative int weigh(@NonNull Object k, @NonNull Object v) {
                        //这里为了简单,直接以 value 为权重
                        return (int) v;
                    }
                })
                .removalListener(new RemovalListener<Object, Object>() {
                    @Override
                    public void onRemoval(@Nullable Object k, @Nullable Object v, @NonNull RemovalCause removalCause) {
                        System.out.println("removed " + k + " cause " + removalCause.toString());
                    }
                })
                .build();

        for (int i = 0; i < 20; i++) {
            cache.put(i, 10);
        }
        cache.cleanUp();
    }

结果,可以看到,当根据权重规则(我这里设置的缓存的值为权重),总权重超过设定值 150 后,发生了驱逐

removed 0 cause SIZE
removed 2 cause SIZE
removed 4 cause SIZE
removed 1 cause SIZE
removed 3 cause SIZE

Process finished with exit code 0

基于时间(Time-based)

默认的

当缓存写入或者被访问后开始计时失效时间,到达设定时间缓存失效(注意此时是标记为失效状态,不会触发驱逐,而是当缓存达到最大值(Size-based)时或者失效的缓存再次被访问触发驱逐)

LoadingCache<Object, Object> cache = Caffeine.newBuilder()
        //基于时间失效->写入之后开始计时失效
        .expireAfterWrite(10, TimeUnit.SECONDS)
        //or 基于时间失效->访问之后开始计时失效
        //.expireAfterAccess(10, TimeUnit.SECONDS)
        //自定义线程池异步执行 remove 监听
        .executor(Executors.newSingleThreadExecutor())
        .removalListener(new RemovalListener<Object, Object>() {
            @Override
            public void onRemoval(@Nullable Object k, @Nullable Object v, @NonNull RemovalCause removalCause) {
                System.out.println("缓存失效了 removed " + k + " cause " + removalCause.toString());
            }
        })
        //同步加载和手动加载的区别就是在构建缓存时提供一个同步的加载方法
        .build(new CacheLoader<Object, Object>() {
            //单个 key 的值加载
            @Nullable
            @Override
            public Object load(@NonNull Object key) throws Exception {
                System.out.println("---exec load---");
                return key + "_" + System.currentTimeMillis();
            }
        });
//放入缓存
cache.put("k1", "v1");
//准备失效
sleep(15000);
System.out.println("sleep done");
System.out.println("我要开始取失效的缓存了");
Object v1 = cache.get("k1");
System.out.println(v1);
System.exit(1);

结果,可以看到我去取缓存的时候才触发了驱逐操作。

sleep done
我要开始取失效的缓存了
---exec load---
缓存失效了 removed k1 cause EXPIRED
k1_1573719759269

Process finished with exit code 1

源码地址

https://github.com/RockeyCui/learn-flink/tree/dev_1.0.0/caffeine-cache


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 rockeycui@163.com

文章标题:Caffeine cache 实例代码demo

文章字数:1.9k

本文作者:崔石磊(RockeyCui)

发布时间:2019-07-23, 18:32:36

原始链接:https://cuishilei.com/Caffeine cache 实例代码demo.html

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏